package com.ruoyi.mdkt.utils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.mdkt.entity.CourseStudent;
import com.ruoyi.mdkt.entity.Student;
import com.ruoyi.mdkt.service.CourseStudentService;
import com.ruoyi.mdkt.service.StudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author gwl
 */
@Slf4j
@Component
public class RankingUtils {

    @Autowired
    private StudentService studentService;

    @Autowired
    private CourseStudentService courseStudentService;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    /**
     * 分割符
     */
    public static final String SPLIT = "#";
    /**
     * 缓存头
     */
    public static final String PREFIX = "Ranking";

    /**
     * 添加学生到排名
     * @param courseId 课程ID
     * @param userId 用户ID
     * @param name 用户名
     * @param score 成绩
     * @return 添加是否成功
     */

    public Boolean add(long courseId , long userId , String name , BigDecimal score){
        String key = getKey(courseId);
        return Boolean.TRUE.equals(redisTemplate.opsForZSet().add(key, userId + RankingUtils.SPLIT + name, score.doubleValue()));
    }

    /**
     * 删除缓存队列成员
     * @param courseId 课程ID
     * @param userId 用户ID
     * @param name 用户名
     * @return 删除是否成功
     */

    public boolean remove(long courseId ,long userId ,String name){
        String key = getKey(courseId);
        Long remove = redisTemplate.opsForZSet().remove(key, userId + RankingUtils.SPLIT + name);
        if (remove == null) {
            return false;
        }
        return remove == 1;
    }

    /**
     * 删除指定课程排名缓存
     * @param courseId 课程Id
     * @return 删除是否成功
     */

    public Boolean removeAll(Long courseId){
        String key = getKey(courseId);
        Boolean delete = redisTemplate.delete(key);
        assert delete != null;
        return delete;
    }

    /**
     * 获取课程全部排名
     * @param courseId 课程ID
     * @return map的key为排名values为学生信息
     */

    public Map<Integer, Student> getAll(long courseId){

        String key = getKey(courseId);
        Long size = redisTemplate.opsForZSet().size(key);
        assert size != null;
        if (size == 0) {
            size = (long)loadStudent(courseId);
            if (size == 0){
                return null;
            }
        }
        return get(key,1,size);
    }

    private int loadStudent(long courseId) {
        LambdaQueryWrapper<CourseStudent> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CourseStudent::getCourseId,courseId);
        List<CourseStudent> list = courseStudentService.list(wrapper);
        if (list.size() == 0){
            return 0;
        }
        List<Long> userIdList  = new ArrayList<>();
        for (CourseStudent courseStudent : list) {
            userIdList.add(courseStudent.getStudentUserId());
        }
        LambdaQueryWrapper<Student> studentWrapper = new LambdaQueryWrapper<>();
        studentWrapper.in(Student::getUserId,userIdList);
        List<Student> studentList = studentService.list(studentWrapper);
        for (Student student : studentList) {
            add(courseId, student.getUserId(), student.getName(), student.getScore());
        }
        return studentList.size();
    }

    /**
     * 排名分页查询
     * @param courseId 课程ID
     * @param pageNum 页数
     * @param pageSize 每页记录数
     * @return map有pageSize个，map的key为排名values为学生信息
     */

    public Map<Integer, Student> getRange(long courseId,int pageNum,int pageSize){
        String key = getKey(courseId);
        Long size = redisTemplate.opsForZSet().size(key);
        assert size != null;
        if (size == 0) {
            size = (long)loadStudent(courseId);
            if (size == 0){
                return null;
            }
        }
        int begin = (pageNum - 1) * pageSize;
        begin += 1;
        long end = ((long) pageNum * pageSize);
        if (size < end){
            end = size;
        }
        if (begin > end){
            return null;
        }
        return get(key,begin,end);
    }

    private Map<Integer, Student> get(String key,int begin, long end){
        Map<Integer,Student> map = new HashMap<>((int)(end-begin+1));
        for (int i = begin-1; i < end; i++) {
            Set<String> set = redisTemplate.opsForZSet().reverseRange(key, i, i);
            if (set == null){
                continue;
            }
            Object[] strings =  set.toArray();
            Double score = redisTemplate.opsForZSet().score(key, strings[0]);
            String[] split = strings[0].toString().split(RankingUtils.SPLIT);
            Student student = new Student();
            student.setId(Long.valueOf(split[0]));
            student.setName(split[1]);
            assert score != null;
            student.setScore(new BigDecimal(score));
            map.put(i+1,student);
        }
        return map;
    }

    /**
     * 获取用户排名
     * @param courseId 课程ID
     * @param userId 用户ID
     * @param name 用户名
     * @return 排名
     */

    public Long getOne(long courseId , long userId , String name ){
        String key = getKey(courseId);
        String value = userId + RankingUtils.SPLIT + name;
        Long aLong = redisTemplate.opsForZSet().reverseRank(key, value);
        if (aLong == null){
            return null;
        }
        return aLong+1;
    }

    public String getRandomName(long courseId){
        String key = getKey(courseId);
        Long size = redisTemplate.opsForZSet().size(key);
        assert size != null;
        if (size == 0){
            size = (long)loadStudent(courseId);
            if (size == 0){
                return null;
            }
        }
        Random random = new Random();
        int i = random.nextInt(Math.toIntExact(size));
        log.info(i+"");
        Set<String> set = redisTemplate.opsForZSet().reverseRange(key, i, i);
        assert set != null;
        Object[] strings =  set.toArray();
        String[] split = strings[0].toString().split(RankingUtils.SPLIT);
        return split[1];
    }

    private String getKey(long courseId){
        return RankingUtils.PREFIX+RankingUtils.SPLIT+ courseId;
    }


}
